home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Tools 2
/
Amiga Tools 2.iso
/
tools
/
jade
/
src
/
unix_processes.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-03-09
|
35KB
|
1,407 lines
/* unix_processes.c -- Subprocess handling for Unix
Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk>
This file is part of Jade.
Jade is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
Jade is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Jade; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "jade.h"
#include "jade_protos.h"
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/signal.h>
#include <sys/fcntl.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <unistd.h>
#include <termios.h>
_PR void protect_procs(void);
_PR void unprotect_procs(void);
_PR bool proc_notification(void);
static void check_for_zombies(void);
static void read_from_process(int);
_PR int write_to_process(VALUE, u_char *, int);
_PR void proc_mark(VALUE);
_PR void proc_sweep(void);
_PR void proc_prin(VALUE, VALUE);
_PR void sigchld_restart(bool);
_PR void proc_init(void);
_PR void proc_kill(void);
static struct sigaction chld_sigact;
static sigset_t chld_sigset;
struct Proc
{
u_char pr_Type;
char pr_Status; /* PR_?? value */
struct Proc *pr_Next;
/* Chain of all processes waiting to be notified of a change of state. */
struct Proc *pr_NotifyNext;
pid_t pr_Pid;
/* pr_Stdin is where we write, pr_Stdout where we read, they may be the
same. */
int pr_Stdin, pr_Stdout;
VALUE pr_OutputStream;
int pr_ExitStatus;
VALUE pr_NotifyFun;
VALUE pr_Prog;
VALUE pr_Args;
VALUE pr_Dir;
VALUE pr_ConnType;
};
/* <= 0 means process not running, > 0 means could be running... */
#define PR_STOPPED 2 /* waiting to be continued */
#define PR_RUNNING 1 /* running merrily */
#define PR_DEAD 0 /* nothing happening on this obj */
#define PR_EXITED -1 /* process dead but no EOF from pty */
/* Connection types, pty-echo is a pty with the ECHO bit set in c_lflag */
static VALUE sym_pipe, sym_pty, sym_pty_echo;
#define PR_CONN_PTY_P(p) \
(((p)->pr_ConnType == sym_pty) || ((p)->pr_ConnType == sym_pty_echo))
#define PR_CONN_PTY_ECHO_P(p) \
((p)->pr_ConnType == sym_pty_echo)
#define PR_CONN_PIPE_P(p) \
((p)->pr_ConnType == sym_pipe)
/* Handy debugging macro */
#if 0
# define DB(x) fprintf x
#else
# define DB(x)
#endif
static struct Proc *process_chain;
static struct Proc *notify_chain;
static int process_run_count;
/* This semaphorey thing protects all operations done on process structures
from SIGCHLD and the process reaping it causes. */
static int process_mutex = -1;
static bool got_sigchld;
INLINE void
protect_procs(void)
{
process_mutex++;
}
void
unprotect_procs(void)
{
if((process_mutex == 0) && got_sigchld)
{
/* Have to leave (process_mutex == 0) while looking for zombies. */
got_sigchld = FALSE;
check_for_zombies();
}
process_mutex--;
}
/* PR's NotifyFun will be called when possible. This function is safe
to call from signal handlers. */
static void
queue_notify(struct Proc *pr)
{
if(pr->pr_NotifyNext == NULL)
{
pr->pr_NotifyNext = notify_chain;
notify_chain = pr;
}
}
/* Dispatch all queued notification. */
bool
proc_notification(void)
{
if(!notify_chain)
return(FALSE);
protect_procs();
cursor(curr_vw, CURS_OFF);
while(notify_chain != NULL)
{
struct Proc *pr = notify_chain;
notify_chain = pr->pr_NotifyNext;
pr->pr_NotifyNext = NULL;
if(pr->pr_NotifyFun && !NILP(pr->pr_NotifyFun))
funcall(pr->pr_NotifyFun, sym_nil);
}
cursor(curr_vw, CURS_ON);
unprotect_procs();
return(TRUE);
}
/* Checks if any of my children are zombies, takes appropriate action. */
static void
check_for_zombies(void)
{
int status;
pid_t pid;
if(!process_run_count)
return;
while((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0)
{
struct Proc *pr = process_chain;
#ifdef DEBUG
messagef("SIGCHLD: pid %d -- status 0x%x", pid, status);
#endif
while(pr)
{
if((pr->pr_Status > 0) && (pr->pr_Pid == pid))
break;
pr = pr->pr_Next;
}
if(pr)
{
if(WIFSTOPPED(status))
{
pr->pr_Status = PR_STOPPED;
queue_notify(pr);
}
else
{
pr->pr_ExitStatus = status;
process_run_count--;
/* It seems that I can't just nuke the pty once the child's
dead -- there can be data pending on it still. So, I set
pr_Status to an in-between value and hope to get an eof
over pr_Stdin RSN */
if((pr->pr_Stdout != 0) || (pr->pr_Stdin != 0))
pr->pr_Status = PR_EXITED;
else
{
/* No file handles open so just die */
pr->pr_Status = PR_DEAD;
queue_notify(pr);
}
}
}
}
}
static void
sigchld_handler(int sig)
{
if(process_mutex < 0)
check_for_zombies();
else
got_sigchld = TRUE;
}
static void
read_from_process(int fd)
{
struct Proc *pr = process_chain;
protect_procs();
while(pr)
{
if((pr->pr_Status != PR_DEAD) && (pr->pr_Stdout == fd))
break;
pr = pr->pr_Next;
}
if(pr)
{
u_char buf[1025];
int actual;
cursor(curr_vw, CURS_OFF);
do {
if((actual = read(fd, buf, 1024)) > 0)
{
buf[actual] = 0;
if(!NILP(pr->pr_OutputStream))
stream_puts(pr->pr_OutputStream, buf, actual, FALSE);
}
} while((actual > 0) || (errno == EINTR));
if((actual <= 0) && (errno != EWOULDBLOCK) && (errno != EAGAIN))
{
/* We assume EOF */
#ifdef HAVE_X11
FD_CLR(pr->pr_Stdout, &x11_fd_read_set);
x11_fd_read_action[pr->pr_Stdout] = NULL;
#endif
close(pr->pr_Stdout);
if(pr->pr_Stdin && (pr->pr_Stdin != pr->pr_Stdout))
close(pr->pr_Stdin);
pr->pr_Stdout = pr->pr_Stdin = 0;
/* This means that the process has already exited and we were
just waiting for the dregs of its output. */
if(pr->pr_Status < 0)
{
pr->pr_Status = PR_DEAD;
queue_notify(pr);
}
}
cursor(curr_vw, CURS_ON);
}
unprotect_procs();
}
int
write_to_process(VALUE pr, u_char *buf, int bufLen)
{
int act = 0;
if(!PROCESSP(pr))
return(0);
protect_procs();
if(VPROC(pr)->pr_Status > 0)
{
if(VPROC(pr)->pr_Stdin == 0)
cmd_signal(sym_process_error, list_2(pr, MKSTR("No link to input")));
else
{
/* This will block */
act = write(VPROC(pr)->pr_Stdin, buf, bufLen);
if(act < 0)
{
signal_file_error(pr);
act = 0;
}
}
}
else
cmd_signal(sym_process_error, list_2(pr, MKSTR("Not running")));
unprotect_procs();
return(act);
}
static bool
signal_process(struct Proc *pr, int sig, bool do_grp)
{
bool rc = TRUE;
protect_procs();
if(do_grp)
{
if(pr->pr_Stdin && PR_CONN_PTY_P(pr))
{
pid_t gid = tcgetpgrp(pr->pr_Stdin);
if(gid != -1)
kill(-gid, sig);
else if(pr->pr_Status != PR_DEAD)
kill(-pr->pr_Pid, sig);
else
rc = FALSE;
}
else
{
if(pr->pr_Status > 0)
kill(-pr->pr_Pid, sig);
else
rc = FALSE;
}
}
else
{
if(pr->pr_Status > 0)
kill(pr->pr_Pid, sig);
else
rc = FALSE;
}
unprotect_procs();
return(rc);
}
/* This is only called during GC, when the process isn't being referenced.
it will already have been taken out of the chain. */
static void
kill_process(struct Proc *pr)
{
protect_procs();
if(pr->pr_Status != PR_DEAD)
{
if(pr->pr_Status == PR_RUNNING)
{
/* is this too heavy-handed?? */
if(!signal_process(pr, SIGKILL, TRUE))
kill(-pr->pr_Pid, SIGKILL);
waitpid(pr->pr_Pid, &pr->pr_ExitStatus, 0);
process_run_count--;
}
if(pr->pr_Stdout)
{
#ifdef HAVE_X11
FD_CLR(pr->pr_Stdout, &x11_fd_read_set);
x11_fd_read_action[pr->pr_Stdout] = NULL;
#endif
close(pr->pr_Stdout);
}
if(pr->pr_Stdin && (pr->pr_Stdin != pr->pr_Stdout))
close(pr->pr_Stdin);
}
str_free(pr);
unprotect_procs();
}
static int
get_pty(char *slavenam)
{
char c;
int i, master;
struct stat statb;
for(c = FIRST_PTY_LETTER; c < 'z'; c++)
{
for(i = 0; i < 16; i++)
{
sprintf(slavenam, "/dev/pty%c%x", c, i);
if(stat(slavenam, &statb) < 0)
goto none;
if((master = open(slavenam, O_RDWR)) >= 0)
{
slavenam[sizeof("/dev/")-1] = 't';
if(access(slavenam, R_OK | W_OK) == 0)
return(master);
close(master);
}
}
}
none:
cmd_signal(sym_process_error, LIST_1(MKSTR("Can't find spare pty")));
return(-1);
}
/* does the dirty stuff of getting the process running. if SYNC_INPUT
is non-NULL it means to run the process synchronously with it's
stdin connected to the file SYNC_INPUT. Otherwise this function returns
immediately after starting the process. */
static bool
run_process(struct Proc *pr, char **argv, u_char *sync_input)
{
bool rc = FALSE;
protect_procs();
if(pr->pr_Status == PR_DEAD)
{
bool usepty = PR_CONN_PTY_P(pr);
char slavenam[32];
int stdin_fds[2], stdout_fds[2]; /* only used for pipes */
pr->pr_ExitStatus = -1;
if(sync_input != NULL)
{
usepty = FALSE;
pr->pr_ConnType = sym_pipe;
if(pipe(stdout_fds) == 0)
{
stdin_fds[0] = open(sync_input, O_RDONLY);
if(stdin_fds[0] < 0)
{
pr->pr_Stdin = 0;
close(stdout_fds[0]);
close(stdout_fds[1]);
}
else
{
pr->pr_Stdin = stdin_fds[0]; /* fake */
pr->pr_Stdout = stdout_fds[0];
}
}
}
else if(usepty)
{
pr->pr_Stdin = get_pty(slavenam);
pr->pr_Stdout = pr->pr_Stdin;
}
else
{
if(pipe(stdin_fds) == 0)
{
if(pipe(stdout_fds) == 0)
{
pr->pr_Stdin = stdin_fds[1];
pr->pr_Stdout = stdout_fds[0];
}
else
{
close(stdin_fds[0]);
close(stdout_fds[1]);
}
}
}
if(pr->pr_Stdin)
{
switch(pr->pr_Pid = fork())
{
case 0:
if(usepty)
{
int slave;
struct termios st;
if(setsid() < 0)
{
perror("child: setsid()");
exit(255);
}
if((slave = open(slavenam, O_RDWR)) < 0)
{
perror("child: open(slave)");
exit(255);
}
close(pr->pr_Stdin);
dup2(slave, 0);
dup2(slave, 1);
dup2(slave, 2);
if(slave > 2)
close(slave);
#ifdef TIOCSCTTY
ioctl(slave, TIOCSCTTY, 0);
#endif
tcgetattr(0, &st);
st.c_iflag &= ~(ISTRIP | IGNCR | INLCR | IXOFF);
st.c_iflag |= (ICRNL | IGNPAR | BRKINT | IXON);
st.c_oflag &= ~OPOST;
st.c_cflag &= ~CSIZE;
st.c_cflag |= CREAD | CS8;
st.c_lflag &= ~(ECHO | ECHOE | ECHOK | NOFLSH | TOSTOP);
st.c_lflag |= ISIG;
if(PR_CONN_PTY_ECHO_P(pr))
st.c_lflag |= ECHO;
st.c_cc[VMIN] = 1;
st.c_cc[VTIME] = 0;
tcsetattr(0, TCSANOW, &st);
}
else
{
/* startup for pipes */
if(setpgid(0, 0) != 0)
{
perror("setpgid");
exit(255);
}
dup2(stdin_fds[0], 0);
close(stdin_fds[0]);
if(sync_input == NULL)
close(stdin_fds[1]);
dup2(stdout_fds[1], 1);
dup2(stdout_fds[1], 2);
close(stdout_fds[0]);
close(stdout_fds[1]);
}
if(STRINGP(pr->pr_Dir) && (STRING_LEN(pr->pr_Dir) > 0))
chdir(VSTR(pr->pr_Dir));
execvp(argv[0], argv);
perror("child: execvp");
exit(255);
case -1:
perror("fork()");
break;
default:
pr->pr_Status = PR_RUNNING;
if(!usepty)
{
close(stdin_fds[0]);
close(stdout_fds[1]);
}
if(sync_input == NULL)
{
if(pr->pr_Stdin == pr->pr_Stdout)
{
/* So that pr_Stdout can be made non-blocking
set up another fd for writing to. */
if((pr->pr_Stdin = dup(pr->pr_Stdout)) < 0)
{
/* Maybe this is unwise? */
perror("dup(pr->pr_Stdout)");
pr->pr_Stdin = pr->pr_Stdout;
}
}
fcntl(pr->pr_Stdin, F_SETFD, 1);
fcntl(pr->pr_Stdout, F_SETFD, 1);
fcntl(pr->pr_Stdout, F_SETFL, O_NONBLOCK);
#ifdef HAVE_X11
FD_SET(pr->pr_Stdout, &x11_fd_read_set);
x11_fd_read_action[pr->pr_Stdout] = read_from_process;
#endif
process_run_count++;
}
else
{
/* Run synchronously. */
u_char buf[1025];
int actual;
pr->pr_Stdin = 0;
do {
actual = read(pr->pr_Stdout, buf, 1024);
if(actual > 0)
{
buf[actual] = 0;
if(!NILP(pr->pr_OutputStream))
{
stream_puts(pr->pr_OutputStream, buf,
actual, FALSE);
}
}
} while((actual > 0) || (errno == EINTR));
waitpid(pr->pr_Pid, &pr->pr_ExitStatus, 0);
close(pr->pr_Stdout);
pr->pr_Stdout = 0;
pr->pr_Status = PR_DEAD;
queue_notify(pr);
}
rc = TRUE;
break;
}
}
}
else
{
cmd_signal(sym_process_error,
list_2(VAL(pr), MKSTR("Already running")));
}
unprotect_procs();
return(rc);
}
void
proc_mark(VALUE pr)
{
MARKVAL(VPROC(pr)->pr_OutputStream);
MARKVAL(VPROC(pr)->pr_NotifyFun);
MARKVAL(VPROC(pr)->pr_Prog);
MARKVAL(VPROC(pr)->pr_Args);
MARKVAL(VPROC(pr)->pr_Dir);
MARKVAL(VPROC(pr)->pr_ConnType);
}
void
proc_sweep(void)
{
struct Proc *pr;
/* First weed out any unused processes from the notify chain... */
pr = notify_chain;
notify_chain = NULL;
while(pr)
{
if(GC_MARKEDP(VAL(pr)))
{
pr->pr_NotifyNext = notify_chain;
notify_chain = pr;
}
pr = pr->pr_NotifyNext;
}
/* ...then do the normal sweep stuff. */
pr = process_chain;
process_chain = NULL;
while(pr)
{
struct Proc *nxt = pr->pr_Next;
if(!GC_MARKEDP(VAL(pr)))
kill_process(pr);
else
{
GC_CLR(VAL(pr));
pr->pr_Next = process_chain;
process_chain = pr;
}
pr = nxt;
}
}
void
proc_prin(VALUE strm, VALUE obj)
{
struct Proc *pr = VPROC(obj);
u_char buf[40];
stream_puts(strm, "#<process", -1, FALSE);
protect_procs();
switch(pr->pr_Status)
{
case PR_RUNNING:
stream_puts(strm, " running: ", -1, FALSE);
stream_puts(strm, VSTR(pr->pr_Prog), -1, TRUE);
break;
case PR_STOPPED:
stream_puts(strm, " stopped: ", -1, FALSE);
stream_puts(strm, VSTR(pr->pr_Prog), -1, TRUE);
break;
case PR_DEAD:
case PR_EXITED:
if(pr->pr_ExitStatus != -1)
{
sprintf(buf, " exited: 0x%x", pr->pr_ExitStatus);
stream_puts(strm, buf, -1, FALSE);
if(pr->pr_Status == PR_EXITED)
stream_puts(strm, " [waiting for eof]", -1, FALSE);
}
break;
}
unprotect_procs();
stream_putc(strm, '>');
}
_PR VALUE cmd_make_process(VALUE stream, VALUE fun, VALUE dir, VALUE prog, VALUE args);
DEFUN("make-process", cmd_make_process, subr_make_process, (VALUE stream, VALUE fun, VALUE dir, VALUE prog, VALUE args), V_Subr5, DOC_make_process) /*
::doc:make_process::
make-process [OUTPUT-STREAM] [FUN] [DIR] [PROGRAM] [ARGS]
<UNIX-ONLY>
Creates a new process-object, OUTPUT-STREAM is where all output from this
process goes, FUN is a function to call each time the process running
on this object changes state. DIR is the process' current directory,
PROGRAM the filename of the program to run and ARGS a list of arguments
passed to the process.
Any of the arguments may be unspecified, in which case they can be set
either by the functions provided or by the function called to create the
actual running process.
::end:: */
{
struct Proc *pr = str_alloc(sizeof(struct Proc));
if(pr)
{
pr->pr_Type = V_Process;
pr->pr_Next = process_chain;
process_chain = pr;
pr->pr_NotifyNext = NULL;
pr->pr_Status = PR_DEAD;
pr->pr_Pid = 0;
pr->pr_Stdin = pr->pr_Stdout = 0;
pr->pr_ExitStatus = -1;
pr->pr_OutputStream = stream;
pr->pr_NotifyFun = fun;
pr->pr_Prog = prog;
pr->pr_Args = args;
pr->pr_Dir = dir;
pr->pr_ConnType = sym_pipe;
return(VAL(pr));
}
return(mem_error());
}
_PR VALUE cmd_start_process(VALUE arg_list);
DEFUN("start-process", cmd_start_process, subr_start_process, (VALUE arg_list), V_SubrN, DOC_start_process) /*
::doc:start_process::
start-process [PROCESS] [PROGRAM] [ARGS...]
<UNIX-ONLY>
Starts a process running on process-object PROCESS. The child-process runs
asynchronously with the editor. If PROCESS is unspecified the make-process
function will be called (with zero arguments) to create one.
PROGRAM is the filename of the binary image, it will be searched for in
all directories listed in the `PATH' environment variable.
ARGS are the arguments to give to the process.
If any of the optional parameters are unspecified they should have been
set in the PROCESS prior to calling this function.
::end:: */
{
struct Proc *pr = NULL;
VALUE res = sym_nil;
protect_procs();
if(CONSP(arg_list))
{
if(PROCESSP(VCAR(arg_list)))
pr = VPROC(VCAR(arg_list));
arg_list = VCDR(arg_list);
}
if(pr == NULL)
{
pr = VPROC(cmd_make_process(sym_nil, sym_nil, sym_nil,
sym_nil, sym_nil));
if(pr == NULL)
{
unprotect_procs();
return(NULL);
}
}
if(CONSP(arg_list))
{
if(STRINGP(VCAR(arg_list)))
pr->pr_Prog = VCAR(arg_list);
arg_list = VCDR(arg_list);
if(CONSP(arg_list))
pr->pr_Args = arg_list;
}
if(!STRINGP(pr->pr_Prog))
res = cmd_signal(sym_process_error, list_2(MKSTR("No program"),
VAL(pr)));
else
{
int numargs = list_length(pr->pr_Args) + 1;
char **argv = str_alloc(sizeof(char *) * (numargs + 1));
if(argv)
{
int i;
arg_list = pr->pr_Args;
argv[0] = VSTR(pr->pr_Prog);
for(i = 1; i < numargs; i++)
{
if(STRINGP(VCAR(arg_list)))
argv[i] = VSTR(VCAR(arg_list));
else
argv[i] = "";
arg_list = VCDR(arg_list);
}
argv[i] = NULL;
if(run_process(pr, argv, NULL))
res = VAL(pr);
else
{
res = cmd_signal(sym_process_error, list_2(MKSTR("Can't start"),
VAL(pr)));
}
str_free(argv);
}
}
unprotect_procs();
return(res);
}
_PR VALUE cmd_run_process(VALUE arg_list);
DEFUN("run-process", cmd_run_process, subr_run_process, (VALUE arg_list), V_SubrN, DOC_run_process) /*
::doc:run_process::
run-process [PROCESS] [IN-FILE] [PROGRAM] [ARGS...]
<UNIX-ONLY>
Starts a process running on process-object PROCESS. Waits for the child to
exit, then returns the exit-value of the child. If PROCESS is unspecified
the make-process function will be called (with zero arguments) to create one.
IN-FILE is the name of the file to connect to the process' standard input,
if this is not defined `/dev/null' is used.
PROGRAM is the filename of the binary image, it will be searched for in
all directories listed in the `PATH' environment variable.
ARGS are the arguments to give to the process.
If any of the optional parameters are unspecified they should have been
set in the PROCESS prior to calling this function.
::end:: */
{
struct Proc *pr = NULL;
VALUE res = sym_nil, infile = MKSTR("/dev/null");
protect_procs();
if(CONSP(arg_list))
{
if(PROCESSP(VCAR(arg_list)))
pr = VPROC(VCAR(arg_list));
arg_list = VCDR(arg_list);
}
if(pr == NULL)
{
pr = VPROC(cmd_make_process(sym_nil, sym_nil, sym_nil,
sym_nil, sym_nil));
if(pr == NULL)
{
unprotect_procs();
return(NULL);
}
}
if(CONSP(arg_list))
{
if(STRINGP(VCAR(arg_list)))
infile = VCAR(arg_list);
arg_list = VCDR(arg_list);
if(CONSP(arg_list))
{
if(STRINGP(VCAR(arg_list)))
pr->pr_Prog = VCAR(arg_list);
arg_list = VCDR(arg_list);
if(CONSP(arg_list))
pr->pr_Args = arg_list;
}
}
if(!STRINGP(pr->pr_Prog))
res = cmd_signal(sym_process_error, LIST_2(MKSTR("No program"),
VAL(pr)));
else if(!file_exists(VSTR(infile)))
res = signal_file_error(infile);
else
{
int numargs = list_length(pr->pr_Args) + 1;
char **argv = str_alloc(sizeof(char *) * (numargs + 1));
if(argv)
{
int i;
arg_list = pr->pr_Args;
argv[0] = VSTR(pr->pr_Prog);
for(i = 1; i < numargs; i++)
{
if(STRINGP(VCAR(arg_list)))
argv[i] = VSTR(VCAR(arg_list));
else
argv[i] = "";
arg_list = VCDR(arg_list);
}
argv[i] = NULL;
if(run_process(pr, argv, VSTR(infile)))
res = make_number(pr->pr_ExitStatus);
else
{
res = cmd_signal(sym_process_error, list_2(MKSTR("Can't run"),
VAL(pr)));
}
str_free(argv);
}
}
unprotect_procs();
return(res);
}
_PR VALUE cmd_signal_process(VALUE proc, VALUE sig, VALUE grp);
DEFUN("signal-process", cmd_signal_process, subr_signal_process, (VALUE proc, VALUE sig, VALUE grp), V_Subr3, DOC_signal_process) /*
::doc:signal_process::
signal-process PROCESS SIGNAL [SIGNAL-GROUP]
<UNIX-ONLY>
If PROCESS is running asynchronously then send signal number SIGNAL to it.
If SIGNAL-GROUP is non-nil also send the signal to all processes in the
process group of PROCESS.
::end:: */
{
VALUE res = sym_nil;
DECLARE1(proc, PROCESSP);
DECLARE2(sig, NUMBERP);
protect_procs();
if(VPROC(proc)->pr_Status > 0)
{
if(signal_process(VPROC(proc), VNUM(sig), !NILP(grp)))
res = sym_t;
}
else
res = cmd_signal(sym_process_error, list_2(proc, MKSTR("Not running")));
unprotect_procs();
return(res);
}
_PR VALUE cmd_interrupt_process(VALUE proc, VALUE grp);
DEFUN("interrupt-process", cmd_interrupt_process, subr_interrupt_process, (VALUE proc, VALUE grp), V_Subr2, DOC_interrupt_process) /*
::doc:interrupt_process::
interrupt-process PROCESS [SIGNAL-GROUP]
<UNIX-ONLY>
Do (signal-process PROCESS SIGINT SIGNAL-GROUP) or equivalent.
::end:: */
{
return(cmd_signal_process(proc, make_number(SIGINT), grp));
}
_PR VALUE cmd_kill_process(VALUE proc, VALUE grp);
DEFUN("kill-process", cmd_kill_process, subr_kill_process, (VALUE proc, VALUE grp), V_Subr2, DOC_kill_process) /*
::doc:kill_process::
kill-process PROCESS [SIGNAL-GROUP]
<UNIX-ONLY>
Do (signal-process PROCESS SIGKILL SIGNAL-GROUP) or equivalent.
::end:: */
{
return(cmd_signal_process(proc, make_number(SIGKILL), grp));
}
_PR VALUE cmd_stop_process(VALUE proc, VALUE grp);
DEFUN("stop-process", cmd_stop_process, subr_stop_process, (VALUE proc, VALUE grp), V_Subr2, DOC_stop_process) /*
::doc:stop_process::
stop-process PROCESS [SIGNAL-GROUP]
<UNIX-ONLY>
Suspends execution of PROCESS, see `continue-process'. If SIGNAL-GROUP is
non-nil also suspends the processes in the process group of PROCESS.
::end:: */
{
return(cmd_signal_process(proc, make_number(SIGSTOP), grp));
}
_PR VALUE cmd_continue_process(VALUE proc, VALUE grp);
DEFUN("continue-process", cmd_continue_process, subr_continue_process, (VALUE proc, VALUE grp), V_Subr2, DOC_continue_process) /*
::doc:continue_process::
continue-process PROCESS [SIGNAL-GROUP]
<UNIX-ONLY>
Restarts PROCESS after it has been stopped (via `stop-process'). If
SIGNAL-GROUP is non-nil also continues the processes in the process group of
PROCESS.
::end:: */
{
VALUE res = sym_t;
DECLARE1(proc, PROCESSP);
protect_procs();
if(VPROC(proc)->pr_Status == PR_STOPPED)
{
if(signal_process(VPROC(proc), SIGCONT, !NILP(grp)))
{
queue_notify(VPROC(proc));
VPROC(proc)->pr_Status = PR_RUNNING;
res = sym_t;
}
}
else
res = cmd_signal(sym_process_error, list_2(proc, MKSTR("Not stopped")));
unprotect_procs();
return(res);
}
_PR VALUE cmd_process_exit_status(VALUE proc);
DEFUN("process-exit-status", cmd_process_exit_status, subr_process_exit_status, (VALUE proc), V_Subr1, DOC_process_exit_status) /*
::doc:process_exit_status::
process-exit-status PROCESS
<UNIX-ONLY>
Returns the unprocessed exit-status of the last process to be run on the
process-object PROCESS. If PROCESS is currently running, return nil.
::end:: */
{
VALUE res = sym_nil;
DECLARE1(proc, PROCESSP);
protect_procs();
if(VPROC(proc)->pr_Status <= 0)
{
if(VPROC(proc)->pr_ExitStatus != -1)
res = make_number(VPROC(proc)->pr_ExitStatus);
}
unprotect_procs();
return(res);
}
_PR VALUE cmd_process_exit_value(VALUE proc);
DEFUN("process-exit-value", cmd_process_exit_value, subr_process_exit_value, (VALUE proc), V_Subr1, DOC_process_exit_value) /*
::doc:process_exit_value::
process-exit-value PROCESS
<UNIX-ONLY>
Returns the return-value of the last process to be run on PROCESS, or nil if:
a) no process has run on PROCESS
b) PROCESS is still running
c) PROCESS exited abnormally
::end:: */
{
VALUE res = sym_nil;
DECLARE1(proc, PROCESSP);
protect_procs();
if((VPROC(proc)->pr_Status <= 0) && (VPROC(proc)->pr_ExitStatus != -1))
res = make_number(WEXITSTATUS(VPROC(proc)->pr_ExitStatus));
unprotect_procs();
return(res);
}
_PR VALUE cmd_process_id(VALUE proc);
DEFUN("process-id", cmd_process_id, subr_process_id, (VALUE proc), V_Subr1, DOC_process_id) /*
::doc:process_id::
process-id PROCESS
<UNIX-ONLY>
If PROCESS is running, return the process-identifier associated with it
(ie, its pid).
::end:: */
{
VALUE res = sym_nil;
DECLARE1(proc, PROCESSP);
protect_procs();
if(VPROC(proc)->pr_Status > 0)
res = make_number(VPROC(proc)->pr_Pid);
unprotect_procs();
return(res);
}
_PR VALUE cmd_process_running_p(VALUE proc);
DEFUN("process-running-p", cmd_process_running_p, subr_process_running_p, (VALUE proc), V_Subr1, DOC_process_running_p) /*
::doc:process_running_p::
process-running-p PROCESS
<UNIX-ONLY>
Return t if PROCESS is running.
::end:: */
{
VALUE res;
DECLARE1(proc, PROCESSP);
protect_procs();
if(VPROC(proc)->pr_Status == PR_RUNNING)
res = sym_t;
else
res = sym_nil;
unprotect_procs();
return(res);
}
_PR VALUE cmd_process_stopped_p(VALUE proc);
DEFUN("process-stopped-p", cmd_process_stopped_p, subr_process_stopped_p, (VALUE proc), V_Subr1, DOC_process_stopped_p) /*
::doc:process_stopped_p::
process-stopped-p PROCESS
<UNIX-ONLY>
Return t if PROCESS has been stopped.
::end:: */
{
VALUE res;
DECLARE1(proc, PROCESSP);
protect_procs();
if(VPROC(proc)->pr_Status == PR_STOPPED)
res = sym_t;
else
res = sym_nil;
unprotect_procs();
return(res);
}
_PR VALUE cmd_process_in_use_p(VALUE proc);
DEFUN("process-in-use-p", cmd_process_in_use_p, subr_process_in_use_p, (VALUE proc), V_Subr1, DOC_process_in_use_p) /*
::doc:process_in_use_p::
process-in-use-p PROCESS
<UNIX-ONLY>
Similar to `process-running-p' except that this returns t even when the
process has stopped, or has exited but the pty connected to `PROCESS' is still
in use.
::end:: */
{
VALUE res;
DECLARE1(proc, PROCESSP);
protect_procs();
if(VPROC(proc)->pr_Status != PR_DEAD)
res = sym_t;
else
res = sym_nil;
unprotect_procs();
return(res);
}
_PR VALUE cmd_processp(VALUE arg);
DEFUN("processp", cmd_processp, subr_processp, (VALUE arg), V_Subr1, DOC_process_p) /*
::doc:process_p::
processp ARG
<UNIX-ONLY>
Return t is ARG is a process-object.
::end:: */
{
if(PROCESSP(arg))
return(sym_t);
return(sym_nil);
}
_PR VALUE cmd_process_prog(VALUE proc);
DEFUN("process-prog", cmd_process_prog, subr_process_prog, (VALUE proc), V_Subr1, DOC_process_prog) /*
::doc:process_prog::
process-prog PROCESS
<UNIX-ONLY>
Return the name of the program in PROCESS.
::end:: */
{
VALUE res;
DECLARE1(proc, PROCESSP);
protect_procs();
res = VPROC(proc)->pr_Prog;
unprotect_procs();
return(res);
}
_PR VALUE cmd_set_process_prog(VALUE proc, VALUE prog);
DEFUN("set-process-prog", cmd_set_process_prog, subr_set_process_prog, (VALUE proc, VALUE prog), V_Subr2, DOC_set_process_prog) /*
::doc:set_process_prog::
set-process-prog PROCESS PROGRAM
<UNIX-ONLY>
Sets the name of the program to run on PROCESS to FILE.
::end:: */
{
DECLARE1(proc, PROCESSP);
DECLARE2(prog, STRINGP);
protect_procs();
VPROC(proc)->pr_Prog = prog;
unprotect_procs();
return(prog);
}
_PR VALUE cmd_process_args(VALUE proc);
DEFUN("process-args", cmd_process_args, subr_process_args, (VALUE proc), V_Subr1, DOC_process_args) /*
::doc:process_args::
process-args PROCESS
<UNIX-ONLY>
Return the list of arguments to PROCESS.
::end:: */
{
VALUE res;
DECLARE1(proc, PROCESSP);
protect_procs();
res = VPROC(proc)->pr_Args;
unprotect_procs();
return(res);
}
_PR VALUE cmd_set_process_args(VALUE proc, VALUE args);
DEFUN("set-process-args", cmd_set_process_args, subr_set_process_args, (VALUE proc, VALUE args), V_Subr2, DOC_set_process_args) /*
::doc:set_process_args::
set-process-args PROCESS ARG-LIST
<UNIX-ONLY>
Set the arguments to PROCESS.
::end:: */
{
DECLARE1(proc, PROCESSP);
if(!NILP(args) && !CONSP(args))
return(signal_arg_error(args, 2));
protect_procs();
VPROC(proc)->pr_Args = args;
unprotect_procs();
return(args);
}
_PR VALUE cmd_process_output_stream(VALUE proc);
DEFUN("process-output-stream", cmd_process_output_stream, subr_process_output_stream, (VALUE proc), V_Subr1, DOC_process_output_stream) /*
::doc:process_output_stream::
process-output-stream PROCESS
<UNIX-ONLY>
Return the stream to which all output from PROCESS is sent.
::end:: */
{
VALUE res;
DECLARE1(proc, PROCESSP);
protect_procs();
res = VPROC(proc)->pr_OutputStream;
unprotect_procs();
return(res);
}
_PR VALUE cmd_set_process_output_stream(VALUE proc, VALUE stream);
DEFUN("set-process-output-stream", cmd_set_process_output_stream, subr_set_process_output_stream, (VALUE proc, VALUE stream), V_Subr2, DOC_set_process_output_stream) /*
::doc:set_process_output_stream::
set-process-output-stream PROCESS STREAM
<UNIX-ONLY>
Set the output-stream of PROCESS to STREAM.
::end:: */
{
DECLARE1(proc, PROCESSP);
protect_procs();
VPROC(proc)->pr_OutputStream = stream;
unprotect_procs();
return(stream);
}
_PR VALUE cmd_process_function(VALUE proc);
DEFUN("process-function", cmd_process_function, subr_process_function, (VALUE proc), V_Subr1, DOC_process_function) /*
::doc:process_function::
process-function PROCESS
<UNIX-ONLY>
Return the function which is called when PROCESS changes state (i.e. it
exits or is stopped).
::end:: */
{
VALUE res;
DECLARE1(proc, PROCESSP);
protect_procs();
res = VPROC(proc)->pr_NotifyFun;
unprotect_procs();
return(res);
}
_PR VALUE cmd_set_process_function(VALUE proc, VALUE fn);
DEFUN("set-process-function", cmd_set_process_function, subr_set_process_function, (VALUE proc, VALUE fn), V_Subr2, DOC_set_process_function) /*
::doc:set_process_function::
set-process-function PROCESS FUNCTION
<UNIX-ONLY>
Set the function which is called when PROCESS changes state to FUNCTION.
::end:: */
{
DECLARE1(proc, PROCESSP);
protect_procs();
VPROC(proc)->pr_NotifyFun = fn;
unprotect_procs();
return(fn);
}
_PR VALUE cmd_process_dir(VALUE proc);
DEFUN("process-dir", cmd_process_dir, subr_process_dir, (VALUE proc), V_Subr1, DOC_process_dir) /*
::doc:process_dir::
process-dir PROCESS
<UNIX-ONLY>
Return the name of the directory which becomes the working directory of
PROCESS when it is started.
::end:: */
{
VALUE res;
DECLARE1(proc, PROCESSP);
protect_procs();
res = VPROC(proc)->pr_Dir;
unprotect_procs();
return(res);
}
_PR VALUE cmd_set_process_dir(VALUE proc, VALUE dir);
DEFUN("set-process-dir", cmd_set_process_dir, subr_set_process_dir, (VALUE proc, VALUE dir), V_Subr2, DOC_set_process_dir) /*
::doc:set_process_dir::
set-process-dir PROCESS DIR
<UNIX-ONLY>
Set the directory of PROCESS to DIR.
::end:: */
{
DECLARE1(proc, PROCESSP);
protect_procs();
VPROC(proc)->pr_Dir = dir;
unprotect_procs();
return(dir);
}
_PR VALUE cmd_process_connection_type(VALUE proc);
DEFUN("process-connection-type", cmd_process_connection_type, subr_process_connection_type, (VALUE proc), V_Subr1, DOC_process_connection_type) /*
::doc:process_connection_type::
process-connection-type PROCESS
<UNIX-ONLY>
Returns a symbol defining the type of stream (i.e. pipe or pty) used to
connect PROCESS with its physical process.
::end:: */
{
VALUE res;
DECLARE1(proc, PROCESSP);
protect_procs();
res = VPROC(proc)->pr_ConnType;
unprotect_procs();
return(res);
}
_PR VALUE cmd_set_process_connection_type(VALUE proc, VALUE type);
DEFUN("set-process-connection-type", cmd_set_process_connection_type, subr_set_process_connection_type, (VALUE proc, VALUE type), V_Subr2, DOC_set_process_connection_type) /*
::doc:set_process_connection_type::
set-process-connection-type PROCESS TYPE
<UNIX-ONLY>
Define how PROCESS communicates with it's child process, TYPE can be one of
the following symbols,
pty Use a pty
pty-echo Similar to `pty' but the ECHO flag is set in the slave
pipe Two pipes are used
This function can only be used when PROCESS is not in use.
::end:: */
{
DECLARE1(proc, PROCESSP);
protect_procs();
if(VPROC(proc)->pr_Status != PR_DEAD)
type = cmd_signal(sym_process_error, list_2(MKSTR("Process in use"),
proc));
else
VPROC(proc)->pr_ConnType = type;
unprotect_procs();
return(type);
}
/* Turns on or off restarted system calls */
void
sigchld_restart(bool flag)
{
if(flag)
{
#ifdef SA_RESTART
chld_sigact.sa_flags |= SA_RESTART;
#else
# ifdef SA_INTERRUPT
chld_sigact.sa_flags &= ~SA_INTERRUPT;
# endif
#endif
}
else
{
#ifdef SA_RESTART
chld_sigact.sa_flags &= ~SA_RESTART;
#else
# ifdef SA_INTERRUPT
chld_sigact.sa_flags |= SA_INTERRUPT;
# endif
#endif
}
sigaction(SIGCHLD, &chld_sigact, NULL);
}
void
proc_init(void)
{
/* Setup SIGCHLD stuff. */
sigemptyset(&chld_sigset);
sigaddset(&chld_sigset, SIGCHLD);
chld_sigact.sa_handler = sigchld_handler;
chld_sigact.sa_mask = chld_sigset;
#ifdef SA_RESTART
chld_sigact.sa_flags = SA_RESTART;
#else
chld_sigact.sa_flags = 0;
#endif
sigaction(SIGCHLD, &chld_sigact, NULL);
/* Is this necessary?? Better safe than core-dumped ;-) */
signal(SIGPIPE, SIG_IGN);
setpgid(0, 0);
INTERN(sym_pipe, "pipe");
INTERN(sym_pty, "pty");
INTERN(sym_pty_echo, "pty-echo");
ADD_SUBR(subr_make_process);
ADD_SUBR(subr_start_process);
ADD_SUBR(subr_run_process);
ADD_SUBR(subr_signal_process);
ADD_SUBR(subr_interrupt_process);
ADD_SUBR(subr_kill_process);
ADD_SUBR(subr_stop_process);
ADD_SUBR(subr_continue_process);
ADD_SUBR(subr_process_exit_status);
ADD_SUBR(subr_process_exit_value);
ADD_SUBR(subr_process_id);
ADD_SUBR(subr_process_running_p);
ADD_SUBR(subr_process_stopped_p);
ADD_SUBR(subr_process_in_use_p);
ADD_SUBR(subr_processp);
ADD_SUBR(subr_process_prog);
ADD_SUBR(subr_set_process_prog);
ADD_SUBR(subr_process_args);
ADD_SUBR(subr_set_process_args);
ADD_SUBR(subr_process_output_stream);
ADD_SUBR(subr_set_process_output_stream);
ADD_SUBR(subr_process_function);
ADD_SUBR(subr_set_process_function);
ADD_SUBR(subr_process_dir);
ADD_SUBR(subr_set_process_dir);
ADD_SUBR(subr_process_connection_type);
ADD_SUBR(subr_set_process_connection_type);
}
void
proc_kill(void)
{
struct Proc *pr;
protect_procs();
pr = process_chain;
while(pr)
{
struct Proc *nxt = pr->pr_Next;
kill_process(pr);
pr = nxt;
}
process_chain = NULL;
#if 0
/* Don't do this since we're technically dead. */
unprotect_procs();
#endif
signal(SIGCHLD, SIG_IGN);
}